# -*- coding: utf-8 -*-
"""
SlideSpeechAuthoring.py

Copyright (c) 2012 SlideSpeech Limited

MIT License: see LICENSE.txt

20120215 Rules added
20120225 Prettier interface. Putting all .png and generated files into same directory.
20120302 German version

"""
from BeautifulSoup import BeautifulStoneSoup # dependency
from cherrypy.lib.static import serve_file # dependency
from zipfile import ZipFile
import BeautifulSoup # dependency
import SlideSpeechConverter
import cPickle
import cgi
import cherrypy # dependency
import codecs
import emailViaGoogle
import forms
import os.path
import scriptParser
import shutil
import sqlite3
import sys
import tempfile
import time
import urllib

# This has to be done per page because the language might
# be different between page views.
#import gettext
#try:
#    t = gettext.translation('slidespeech', 'locale')
#except:
#    t = gettext.NullTranslations()
#
#_ = t.lgettext

if sys.platform.startswith("win"):
    import pythoncom
    import win32com.client
    pass
elif sys.platform.startswith("darwin"):
    # TODO: Neither ODP nor PPT import works on Mac at this time
    pass
else:
    pass
    import odp2png_jg

# pass through email account
GMAIL_LOGIN = 'john.graves.at.uu@gmail.com'
GMAIL_PASSWORD = 'ASDFasdf1'

# Column numbers in all tables
TAG = 0
EMAIL = 1

# Column numbers in logs and scripts tables
PUBLISHEDTIME = 2

# Column numbers in logs table
CURRENTLOG = 3

# Column numbers in slides table
SLIDENUMBER = 2
SLIDESOURCE = 3
SLIDETEXT = 4

__version__ = '2.3 (GR 20120323)'

if not os.path.exists('static'):
    os.makedirs('static')
if not os.path.exists('static'+os.sep +'temp'):
    os.makedirs('static'+os.sep +'temp')
if not os.path.exists('static'+os.sep +'logs'):
    os.makedirs('static'+os.sep +'logs')

# Initialize database if it does not exist
if not os.path.exists('slides.db'):
    connection = sqlite3.connect('slides.db')
    cursor = connection.cursor()
    cursor.execute('CREATE TABLE logs (tag TEXT, email TEXT, publishedTime TEXT, currentLog TEXT);')
    connection.commit()
    """
    
            jsonOutput += '\n       "tag":"'+script[0]+'",'
            jsonOutput += '\n       "title":"'+script[0]+'",'
            jsonOutput += '\n       "author":"'+script[0]+'",'
            jsonOutput += '\n       "downloadURL":"'+script[0]+'",'
            jsonOutput += '\n       "iconURL":"'+script[0]+'",'
            jsonOutput += '\n       "createTimestamp":"'+script[0]+'",'
            jsonOutput += '\n       "downloadURL":"'+script[0]+'",'
            jsonOutput += '\n       "version":"'+script[0]+'",'
            jsonOutput += '\n       "languages":["en"]'
            """
    cursor.execute('CREATE TABLE scripts (tag TEXT, email TEXT, publishedTime TEXT, ' + \
                    'title TEXT, author TEXT, downloadURL TEXT, iconURL TEXT, version TEXT, languages TEXT);')
    connection.commit()
    cursor.execute('CREATE TABLE slides (tag TEXT, email TEXT, slideNumber NUMERIC, slideSource TEXT, slideText TEXT);')
    connection.commit()

# Copied from http://tools.cherrypy.org/wiki/DirectToDiskFileUpload
class myFieldStorage(cgi.FieldStorage):
    """Our version uses a named temporary file instead of the default
    non-named file; keeping it visibile (named), allows us to create a
    2nd link after the upload is done, thus avoiding the overhead of
    making a copy to the destination filename."""

    def make_file(self, binary=None):
        return tempfile.NamedTemporaryFile()


def noBodyProcess():
    """Sets cherrypy.request.process_request_body = False, giving
    us direct control of the file upload destination. By default
    cherrypy loads it to memory, we are directing it to disk."""
    cherrypy.request.process_request_body = False

cherrypy.tools.noBodyProcess = cherrypy.Tool('before_request_body', noBodyProcess)


class ScriptAuthoringPage:

    def index(self):
        # Ask for the script name.
        #return forms.getScriptAndEmail(__version__,"")
        
        # FIXME: where do we get the user's language preference from?
        return forms.uploadPresentationFile(__version__, "en", None)
    
        # Redirect to http://slidespeech2012.pagekite.me/listScripts?email=jg07024@gmail.com
##        return """<html>
##            <head>
##            <script type="text/javascript">
##            function redirect(){
##                window.location = """ + '"listScripts?email=jg07024@gmail.com"\n'+ \
##            """}
##            </script>
##            </head>
##            <body onLoad="redirect()">
##            </body>
##            </html>"""
    index.exposed = True
    #webbrowser.open_new_tab('http://localhost:8080')

    # ----------------------------------------------------------------

    def timestamp(self):
        """Returns YYYYMMDD.HH.MM.SS_xxxxxx where xxxxxx is the fractional part of the day"""
        return time.strftime("%Y%m%d.%H.%M.%S_")+("%f" % time.time()).split(".")[1]

    # ----------------------------------------------------------------

    def fetchSlides(self, tag=None, email=None):
        defaultReturn = forms.getTitle(__version__,"Enter a script name.", email)
        if tag:
            tag=urllib.unquote(tag).decode('utf8')
            if 0<len(tag.strip()):
                connection = sqlite3.connect('slides.db')
                cursor = connection.cursor()

                # find path if published
                cursor.execute('SELECT DISTINCT * FROM scripts WHERE tag=? AND email=?',(tag,email,))
                scriptRecord=cursor.fetchall()
                pathToPublishedHtml = ""
                if 0<len(scriptRecord):
                    pathToPublishedHtml = scriptRecord[0][PUBLISHEDTIME]

                cursor.execute('SELECT * FROM slides WHERE tag=? AND email=?',(tag,email,))
                slideRecords=cursor.fetchall()
                slideList = []
                for slide in slideRecords:
                    slideList.append([slide[SLIDESOURCE],slide[SLIDETEXT]])
                return forms.scriptInputFormWithMessage(__version__,
                                                        "",
                                                        tag,
                                                        slideList,
                                                        "",
                                                        False,
                                                        pathToPublishedHtml,
                                                        email)
        return defaultReturn
    fetchSlides.exposed = True

    # ----------------------------------------------------------------

    def submitSlides(self, **params):
        #return params.keys()
        publishFlag = False
        slideList = []
        slideSources = {}
        slideTexts = {}
        # for testing
        for key in params.keys():
            if key == 'tag':
                tag = params['tag']
            elif key == 'publishFlag':
                publishFlag = True
            elif key == 'email':
                email = params['email']
            elif key.startswith('slideSource'):
                slideSources[key[11:]] = params[key]
            elif key.startswith('textarea'):
                slideTexts[key[8:]] = params[key]

        SQLconnection = sqlite3.connect('slides.db')
        cursor = SQLconnection.cursor()

        # Update scripts table and clear out any prior published files (all except .png)
        cursor.execute('SELECT * FROM scripts WHERE tag=? AND email=?',(tag,email,))
        scriptList = cursor.fetchall()
        publishedTime=""
        for script in scriptList:
            if 0<len(script[PUBLISHEDTIME].strip()):
                self.unpublishScript(script, deleteAll=False)
                # Pass publishedTime so submitted script can find images
                publishedTime=script[PUBLISHEDTIME]
        cursor.execute('DELETE FROM scripts WHERE tag=? AND email=?',(tag,email,))

        # Update slides table
        cursor.execute('DELETE FROM slides WHERE tag=? AND email=?',(tag,email,))
        for i in range(0,len(slideSources)):
            slideList.append([slideSources[str(i)],slideTexts[str(i)]])
            cursor.execute('INSERT INTO slides VALUES (?,?,?,?,?)', (tag,email,i,slideSources[str(i)],slideTexts[str(i)]))
        if not publishFlag:
            # Save script name with no time (unpublished)
            cursor.execute('INSERT INTO scripts VALUES (?,?,?,?,?,?,?,?,?)', (tag,email,"","4","5","6","7","8","9"))
        SQLconnection.commit()

        if publishFlag:
            return self.publishSlidesToHtml(tag=tag,email=email,publishedTime=publishedTime)
        else:
            return forms.scriptInputFormWithMessage(__version__,"",tag,slideList,"",False,"",email)
    submitSlides.exposed = True

    # ----------------------------------------------------------------

    def addSlide(self, tag = None, slideNumber = None, email=None):
        if tag:
            tag=urllib.unquote(tag).decode('utf8')
        if slideNumber:
            slideNumber=urllib.unquote(slideNumber).decode('utf8')
        slideNumber = int(slideNumber)
        connection = sqlite3.connect('slides.db')
        cursor = connection.cursor()
        cursor.execute('SELECT * FROM slides WHERE tag=? AND email=?',(tag,email,))
        slideRecords=cursor.fetchall()
        slideList = []
        for slide in slideRecords:
            slideList.append([slide[SLIDESOURCE],slide[SLIDETEXT]])
        slideListBefore = slideList[:slideNumber]
        slideListAfter = slideList[slideNumber:]
        slideList = []
        slideList.extend(slideListBefore)
        slideList.append(['','']) # New Slide
        slideList.extend(slideListAfter)
        return forms.scriptInputFormWithMessage(__version__,"",tag,slideList,"",True,"",email)
    addSlide.exposed = True

    # ----------------------------------------------------------------

    def changeSlide(self, tag = None, slideNumber = None, email=None):
        if tag:
            tag=urllib.unquote(tag).decode('utf8')
        if slideNumber:
            slideNumber=urllib.unquote(slideNumber).decode('utf8')
        slideNumber = int(slideNumber)
        connection = sqlite3.connect('slides.db')
        cursor = connection.cursor()
        cursor.execute('SELECT * FROM slides WHERE tag=? AND email=?',(tag,email,))
        slideRecords=cursor.fetchall()
        slideList = []
        for slide in slideRecords:
            slideList.append([slide[SLIDESOURCE],slide[SLIDETEXT]])
        slideListBefore = slideList[:slideNumber]
        slideListAfter = slideList[slideNumber:]
        slideList = []
        slideList.extend(slideListBefore)
        oldSlide = ""
        if 0<len(slideListAfter):
            slideList.append(['',slideListAfter[0][1]]) # New Slide Needed
            oldSlide = slideListAfter[0][0]
        if 1<len(slideListAfter):
            slideList.extend(slideListAfter[1:])
        return forms.scriptInputFormWithMessage(__version__,"",tag,slideList,oldSlide,True,"",email)
    changeSlide.exposed = True

    # ----------------------------------------------------------------

    def deleteSlide(self, tag = None, slideNumber = None, time = None, email = None):
        if tag:
            tag=urllib.unquote(tag).decode('utf8')
        if slideNumber:
            slideNumber=urllib.unquote(slideNumber).decode('utf8')
            message = "Deleted slide."
            slideNumber = int(slideNumber)
        connection = sqlite3.connect('slides.db')
        cursor = connection.cursor()
        cursor.execute('SELECT * FROM slides WHERE tag=? AND email=?',(tag,email,))
        slideRecords=cursor.fetchall()
        slideList = []
        for slide in slideRecords:
            slideList.append([slide[SLIDESOURCE],slide[SLIDETEXT]])
        slideList.remove(slideList[slideNumber])

        #Replace records with renumbered slides
        cursor.execute('DELETE FROM slides WHERE tag=? AND email=?', (tag,email,))
        for i in range(0,len(slideList)):
            cursor.execute('INSERT INTO slides VALUES (?,?,?,?,?)', (tag,email,i,slideList[i][0],slideList[i][1]))
        connection.commit()

        return forms.scriptInputFormWithMessage(__version__,message,tag,slideList,"",False,"",email)
    deleteSlide.exposed = True

    # ----------------------------------------------------------------

    def fetchScriptToView(self, tag = None, email=None):
        if tag:
            tag=urllib.unquote(tag).decode('utf8')
            connection = sqlite3.connect('slides.db')
            cursor = connection.cursor()
            cursor.execute('SELECT DISTINCT * FROM scripts WHERE tag=? AND email=?',(tag,email,))
            scriptRecord=cursor.fetchall()
            url = os.sep +"static"+os.sep +scriptRecord[0][1]+os.sep +"odpName.htm"

        return forms.fetchScriptToView(url)
    fetchScriptToView.exposed = True

    # ----------------------------------------------------------------

    def importScript(self, email=None):
        if email==None or 0==len(email.strip()):
            return forms.getScriptAndEmail(__version__,"Please supply your e-mail address.","")
        return forms.getScript(__version__,"",email)
    importScript.exposed = True

    # ----------------------------------------------------------------

    def deleteScript(self, tag = None, email = None):
        if tag:
            tag=urllib.unquote(tag).decode('utf8')
        if email:
            email=urllib.unquote(email).decode('utf8')

        # update slides and scripts tables
        connection = sqlite3.connect('slides.db')
        cursor = connection.cursor()
        cursor.execute('SELECT * FROM scripts WHERE tag=? AND email=?', (tag,email,))
        scriptToDeleteList = cursor.fetchall()
        self.unpublishScript(scriptToDeleteList[0], deleteAll=True)

        cursor.execute('DELETE FROM slides WHERE tag=? AND email=?', (tag,email,))
        cursor.execute('DELETE FROM scripts WHERE tag=? AND email=?', (tag,email,))
        connection.commit()

        # show updated list of scripts
        cursor.execute('SELECT * FROM scripts WHERE email=?', (email,))
        scriptList = cursor.fetchall()
        scriptList.sort()
        if email==None or 0==len(email) or -1==email.find('@'):
            return forms.listScripts(__version__,
                                     "Script not deleted. Matching e-mail required.",
                                     scriptList,
                                     "",
                                     "",
                                     email)
        else:
            return forms.listScripts(__version__,"",scriptList,"","",email)
    deleteScript.exposed = True

    # ----------------------------------------------------------------

    def listScripts(self, email=None):
        connection = sqlite3.connect('slides.db')
        cursor = connection.cursor()
        cursor.execute('SELECT * FROM scripts WHERE email=?', (email,))
        scriptList = cursor.fetchall()
        scriptList.sort()
        return forms.listScripts(__version__,"",scriptList,"","",email)
    listScripts.exposed = True

    # ----------------------------------------------------------------

    def listScriptsJSON(self, email=None):
        """
        [
   {
       "tag":"biography",
       "title":"My Life",
       "author":"John Graves"
       "downloadURL":"http://www.my-download-url.com",
       "iconURL":"http://www.img.my-download-url.com/img.png",
       "createTimestamp":"01/01/1970T14:22:45",
       "version":1.6,
       "languages": 
           [
               "en", "nl", "fr"
           ]
   }
]
        """
    
        connection = sqlite3.connect('slides.db')
        cursor = connection.cursor()
        cursor.execute('SELECT * FROM scripts WHERE email=?', (email,))
        scriptList = cursor.fetchall()
        scriptList.sort()
        
        jsonOutput = """[
   {"""
        for i, script in enumerate(scriptList):
            jsonOutput += '\n       "tag":"'+script[0]+'",'
            jsonOutput += '\n       "title":"'+script[3]+'",'
            jsonOutput += '\n       "author":"'+script[4]+'",'
            jsonOutput += '\n       "downloadURL":"'+script[5]+'",'
            jsonOutput += '\n       "iconURL":"'+script[6]+'",'
            jsonOutput += '\n       "createTimestamp":"'+script[2]+'",'
            jsonOutput += '\n       "version":"'+script[7]+'",'
            jsonOutput += '\n       "languages":["'+script[8]+'"]'
            if i<len(scriptList)-1:
                jsonOutput += '\n   },{'
            else:
                jsonOutput += """
   }
]\n"""
        return jsonOutput
    listScriptsJSON.exposed = True

    # ----------------------------------------------------------------

    def listScriptsForCatalog(self,email):
        connection = sqlite3.connect('slides.db')
        cursor = connection.cursor()
        cursor.execute('SELECT * FROM slides WHERE slideNumber = 0 AND email=?',(email,))
        scriptList = cursor.fetchall()
        scriptList.sort()
        scriptCatalog = ""
        for script in scriptList:
            scriptCatalog += script[0]+"|"+script[2]+"\n"
        return scriptCatalog
    listScriptsForCatalog.exposed = True

    # ----------------------------------------------------------------

    def uploadODP(self, myFile=None, email=None, title=None, author=None):
        if email==None or 0==len(email.strip()):
            return forms.getScriptAndEmail(__version__,"Please supply your e-mail address.")
        if -1==email.find('@'):
            return forms.getScriptAndEmail(__version__,"Please supply a valid e-mail address.")
        if type(myFile)==type(None):
            return forms.getScriptAndEmail(__version__,"Please select an Open Document Presentation (.odp) file.")
        (sourcepath, ODPFile) = os.path.split(myFile.filename)
        (ODPFileStem, ODPFileExtension) = os.path.splitext(ODPFile)
        if not (ODPFileExtension in (".ODP",".odp")):
            return forms.getScriptAndEmail(__version__, "Please select an Open Document Presentation (.odp) file.")
        else:

            # Create time stamped temp directory
            publishedTime = self.timestamp()
            tempDir = os.getcwd()+os.sep+"static"+os.sep+publishedTime+os.sep
            os.makedirs(tempDir)

            # Save ODP into new directory
            tempFile = tempDir+"temp"+ODPFileExtension
            savedODP = open(tempFile,"wb")
            while True:
                data = myFile.file.read(8192)
                if not data:
                    break
                savedODP.write(data)
            savedODP.close()

            # Process ODP into PNG files and a script.txt file
            text = self.processODP(tempFile, tempDir, title, author)

            # Sequence the script and post records to SQL database
            seq = scriptParser.parseText(text)
            shutil.copyfile("output.json",tempDir+"output.json")
#            for i, question in enumerate(seq.sequence):
#                shutil.copyfile("sample.png",tempDir+"Slide"+str(i)+".png")
                    
            outputFile = ZipFile(tempDir+"output.zip",'w')
            savePath = os.getcwd()
            os.chdir(tempDir)
            tempPathDirectoryList = os.listdir('.')
            for fileName in tempPathDirectoryList:
                fileNameStem, fileExtension = os.path.splitext(fileName)
                if fileExtension in (".png") or fileName=="output.json":
                    outputFile.write(fileName)
            outputFile.close()
            os.chdir(savePath)

            connection = sqlite3.connect('slides.db')
            cursor = connection.cursor()
            slideList = []
            for question in seq.sequence:
                staticAt = tempDir.find('static')
                if 0<len(question.linkToShow):
                    slideList.append([tempDir[staticAt:]+question.linkToShow,
                                 question.unparsedQuestion])
                else:
                    slideList.append(["",
                                 question.unparsedQuestion])

            #Replace records (if any) with renumbered slides
            cursor.execute('DELETE FROM slides WHERE tag=?', (ODPFileStem,))
            for i in range(0,len(slideList)):
                cursor.execute('INSERT INTO slides VALUES (?,?,?,?,?)',
                              (ODPFileStem,email,i,slideList[i][0],slideList[i][1]))
            connection.commit()
            self.publishSlidesToHtml(tag=ODPFileStem, email=email,publishedTime=publishedTime)
            scriptList = [(ODPFileStem,email,publishedTime)]
            return forms.listScripts(__version__,
                                     "Presentation published. Check e-mail.",
                                     scriptList,
                                     ODPFileStem,
                                     publishedTime,
                                     email)

#            return forms.scriptInputFormWithMessage(__version__,
#                                                    "",
#                                                    ODPFileStem,
#                                                    slideList,
#                                                    "",
#                                                    False,
#                                                    "",
#                                                    email)
    uploadODP.exposed = True

    # ----------------------------------------------------------------

    def joinContents(self, textPList):
        """Combine tagged XML into single string

    Needs to handle this from PowerPoint:
        <text:p text:style-name="a785" text:class-names="" text:cond-style-name="">
         <text:span text:style-name="a783" text:class-names="">Voice over 1</text:span>
         <text:span text:style-name="a784" text:class-names=""/>
        </text:p>

    or worse, this:
        <text:p text:style-name="a786" text:class-names="" text:cond-style-name="">
         <text:span text:style-name="a783" text:class-names="">
          Voice
          <text:s text:c="1"/>
         </text:span>
         <text:span text:style-name="a784" text:class-names="">
          over 1
          <text:s text:c="1"/>
          asdf
         </text:span>
         <text:span text:style-name="a785" text:class-names=""/>
        </text:p>

        """
        # item is list of all the XML for a single slide
        joinedItems = ""
        if len(textPList)>0:
            textItems = []
            i = 0
            for textP in textPList:
                textSpans = []
                # break the XML into a list of tagged pieces (text:span)
                for item in textP:
                    if type(item)==BeautifulSoup.Tag:
                        tagContents = item.contents
                        if type(tagContents)==type([]):
                            for item2 in tagContents:
                                if type(item2)==BeautifulSoup.Tag:
                                    textSpans.append([item2.contents])
                                else:
                                    textSpans.append([item2])
                        else:
                            textSpans.append([tagContents])
                    else:
                        textSpans.append([item])

                # flatten list
                textSpans1 = [item for sublist in textSpans for item in sublist]
                # clean up
                textSpans1b = []
                for item in textSpans1:
                    if type(item)==BeautifulSoup.NavigableString:
                        textSpans1b.append(item)
                    elif type(item)==type([]):
                        if len(item)==0:
                            pass
                        elif len(item)==1:
                            textSpans1b.append(item[0])
                        else:
                            for itemInList in item:
                                textSpans1b.append(itemInList)
                # find the contents of these pieces if they are still tagged (text:s)
                textSpans2 = []
                for textSpan in textSpans1b:
                    if type(textSpan)==BeautifulSoup.Tag:
                        textSpans2.append(textSpan.text)
                    else:
                        if (type(textSpan)==type([]) and len(textSpan)>0):
                            textSpans2.append(unicode(textSpan[0]))
                        else:
                            textSpans2.append(unicode(textSpan))

                justText = u""
                for item in textSpans2:
                    # deal with single quote and double quotes and dashes
                    # \u2018 LEFT SINGLE QUOTATION MARK
                    justText = justText + item + u" "
                textItems.append(justText)
            joinedItems = "\n".join(textItems)
        return joinedItems

    # ----------------------------------------------------------------

    def processODP(self, tempFile, tempDir, title, author):
        if os.path.exists(tempFile):
            # create .png files
            args = []
            args.append(tempFile)
#             if not sys.platform.startswith("darwin"):
#                 odp2png_jg.runparseNoOpts(args, tempDir=tempDir)

            # Look for .png files (slide images) in the tempDir subdirectory
            dir = os.listdir(tempDir)
            imageFileList = [file for file in dir if file.lower().endswith(".png")]

            odpName = tempFile.replace(".odp","")
            odp = ZipFile(tempFile,'r')
            f = odp.read(u'content.xml')
            soup = BeautifulStoneSoup(f)
            notes = soup.findAll(attrs={"presentation:class":"notes"})
            noteTextPLists = [item.findAll("text:p") for item in notes]
            noteText = [self.joinContents(noteTextPList) for noteTextPList in noteTextPLists]
            f = open("input2.txt","w")
            f.write(" ".join(noteText))
            f.close()
        else:
            return ""

        # Create script.txt file
        scriptFile = codecs.open(tempDir+'script.txt', encoding='utf-8', mode='w')
        scriptFile.write("""#[path=]
#
#     Script created with SlideSpeech from ODP version """+
__version__+
"\n#     http://slidespeech.org\n"+
"#     Date: "+time.strftime("%a, %d %b %Y %H:%M:%S", time.localtime())+"""
#
#     Title:
#     Author:
#
#     SlideSpeech Slide show version
#     http://
#
#     SlideSpeech Video version
#     http://
#
#     SlideSpeech script:
""")
        onImg = 0
        minNum = 0
        imageFileSuffix = "png"
        imageFilePrefix = "Slide"
        questionMode = False
        for item in noteText:
            if onImg-minNum == 0: # first slide
                # insert line with link to first slide image after parameter lines
                # For example, noteText could start with [path=...]
                # Unless in question mode, [questions=on]
                lines = item.split("\n")
                for linenum, line in enumerate(lines):
                    line = line.strip()
                    if 0<len(line):
                        if line.startswith("["):
                            scriptFile.write(line+"\n")
                            if line.startswith("[questions=on]"):
                                questionMode = True
                            elif line.startswith("[questions=off]"):
                                questionMode = False
                        else:
                            scriptFile.write(imageFilePrefix+str(onImg)+"."+imageFileSuffix+"\n")
                            scriptFile.write(line+"\n")
                    else:
                        scriptFile.write("\n")
            else:
                if not questionMode:
                    # Add a line with a link to each slide
                    scriptFile.write(imageFilePrefix+str(onImg)+"."+imageFileSuffix+"\n")
                # followed by the voice over text for the slide
                scriptFile.write(item+"\n")
                # set questionMode appropriately
                lines = item.split("\n")
                for line in lines:
                    line = line.strip()
                    if 0<len(line):
                        if line.startswith("["):
                            if line.startswith("[questions=on]"):
                                questionMode = True
                            elif line.startswith("[questions=off]"):
                                questionMode = False

            scriptFile.write("\n")
            onImg += 1
        scriptFile.close()

        outFile = codecs.open(tempDir+'script.txt',encoding='utf-8',mode='r')
        text = outFile.readlines()
        outFile.close()
        return text

    # ----------------------------------------------------------------

    def uploadPPT(self, myFile=None, email = None):
        (sourcepath, PPTFile) = os.path.split(myFile.filename)
        (PPTFileStem, PPTFileExtension) = os.path.splitext(PPTFile)
        if not (PPTFileExtension in (".ppt",".pptx")):
            return forms.getScript(__version__, "Select a PowerPoint file.")
        else:

            # Create time stamped temp directory
            publishedTime = self.timestamp()
            tempDir = os.getcwd()+os.sep+"static"+os.sep+publishedTime+os.sep
            os.makedirs(tempDir)

            # Save PPT into new directory
            tempFile = tempDir+"temp"+PPTFileExtension
            savedPPT = open(tempFile,"wb")
            while True:
                data = myFile.file.read(8192)
                if not data:
                    break
                savedPPT.write(data)
            savedPPT.close()

            # Process PPT into JPG files and a script.txt file
            text = self.processPPT(tempFile, tempDir)

            # Sequence the script and post records to SQL database
            seq = scriptParser.parseText(text)
            connection = sqlite3.connect('slides.db')
            cursor = connection.cursor()
            slideList = []
            for question in seq.sequence:
                questionTexts = "\n".join(question.questionTexts)
                staticAt = tempDir.find('static')
                slideList.append([tempDir[staticAt:]+question.linkToShow,
                                 unicode(questionTexts,'utf_8')])

            #Replace records (if any) with renumbered slides
            cursor.execute('DELETE FROM slides WHERE tag=? AND email=?', (PPTFileStem,email,))
            for i in range(0,len(slideList)):
                cursor.execute('INSERT INTO slides VALUES (?,?,?,?,?)',
                              (PPTFileStem,email,i,slideList[i][0],slideList[i][1]))
            connection.commit()

            self.publishSlidesToHtml(tag=PPTFileStem, email=email,publishedTime=publishedTime)

            return forms.scriptInputFormWithMessage(__version__,
                                                    "",
                                                    PPTFileStem,
                                                    slideList,
                                                    "",
                                                    False,
                                                    "",
                                                    email)
    uploadPPT.exposed = True

    # ----------------------------------------------------------------

    def processPPT(self, tempFile, tempDir):
        '''Breaks PPT into images and text and returns contents of script.txt'''
        if sys.platform.startswith("win") and os.path.exists(tempFile):
            # create .jpg files
            slideNotes = []
            pythoncom.CoInitialize()
            Application = win32com.client.Dispatch("PowerPoint.Application")
            Application.Visible = True
            Presentation = Application.Presentations.Open(tempFile)
            onSlide = 0
            for Slide in Presentation.Slides:
                 imageName = "Slide" + str(onSlide) + ".jpg"
                 onSlide += 1
                 Slide.Export(tempDir+imageName,"JPG",800,600)

                 for Shape in Slide.NotesPage.Shapes:
                    if Shape.HasTextFrame:
                        if Shape.TextFrame.HasText:
                            text = Shape.TextFrame.TextRange.Text
                            if not text.isdigit():
                                slideNotes.append(text)
            Application.Quit()

            # Look for .jpg files (slide images) in the odpName subdirectory
            dir = os.listdir(tempDir)
            imageFileList = [file for file in dir if file.lower().endswith(".jpg")]

            outFile = open(tempDir+'script.txt',"w")
            onSlide = 0
            for item in slideNotes:
                imageName = "Slide" + str(onSlide) + ".jpg\n"
                onSlide += 1
                outFile.write(imageName)
                outFile.write(item + "\n\n")
            outFile.close()
            outFile = open(tempDir+'script.txt',"r")
            text = outFile.readlines()
            outFile.close()
        return text

    # ----------------------------------------------------------------

    def importSlides(self, url = None, title = None, scriptPath = None, email= None):
        defaultReturn = forms.getScript(__version__,"Enter a URL for a script.",email)
        if scriptPath:
            scriptPath=urllib.unquote(scriptPath).decode('utf8')
        else:
            scriptPath=""
        if title:
            projectDirectory=urllib.unquote(title).decode('utf8')
        else:
            projectDirectory=""
        if url:
            urlString=urllib.unquote(url).decode('utf8')
            (path,scriptFile) = os.path.split(urlString)
            # get title (tag) for script from url
            if 0==len(projectDirectory.strip()):
                (stem,projectDirectory) = os.path.split(path)
            seq = scriptParser.parseHtml(url)
            if seq==None:
                # Bad url or other issue
                return defaultReturn
            connection = sqlite3.connect('slides.db')
            cursor = connection.cursor()
            slideList = []
            for question in seq.sequence:
                if 0<len(question.answers):
                    questionTexts = '[questions=on]\n'
                    questionTexts += "\n".join(question.questionTexts)
                    questionTexts += "\n"
                    for answer in question.answers:
                        questionTexts += answer.answerText + ' ;'
                        if 0<answer.action:
                            questionTexts += ';'*answer.action
                        elif 0>answer.action:
                            questionTexts += str(answer.action)
                        questionTexts += ' ' + answer.responseText + '\n'
                    questionTexts += '[questions=off]\n'
                else:
                    questionTexts = "\n".join(question.questionTexts)
                imagePath = question.linkToShow
                if 0<len(imagePath):
                    imagePath = question.pathToImageFiles + imagePath
                slideList.append([imagePath,
                                 unicode(questionTexts,'utf_8')])

            #Replace records (if any) with renumbered slides
            cursor.execute('DELETE FROM slides WHERE tag=?', (projectDirectory,))
            for i in range(0,len(slideList)):
                cursor.execute('INSERT INTO slides VALUES (?,?,?,?,?)',
                              (projectDirectory,email,i,scriptPath+slideList[i][0],slideList[i][1]))
            connection.commit()

            return forms.scriptInputFormWithMessage(__version__,
                                                    "",
                                                    projectDirectory,
                                                    slideList,
                                                    "",
                                                    False,
                                                    "",
                                                    email)
        return defaultReturn
    importSlides.exposed = True

    # ----------------------------------------------------------------

    def uploadScript(self, myFile, title = None):
        (path, scriptFile) = os.path.split(myFile.filename)
        if "script.txt"<>scriptFile:
            return forms.getScript(__version__, "Script file should be called script.txt.")
        else:
            savedImage = open("script.txt","w")
            while True:
                data = myFile.file.read(8192)
                if not data:
                    break
                savedImage.write(data)
            savedImage.close()
            return self.importSlides(url="script.txt", title = title, scriptPath = path)
    uploadScript.exposed = True

    # ----------------------------------------------------------------

    def unpublishScript(self, script, deleteAll=False):
        '''Delete generated script.txt, HTML, ZIP and audio files, leaving only image files'''
        # DEBUG
        return
        if not deleteAll:
            # copy only the .png or .jpg files
            if not os.path.exists('static'+os.sep+'temp'+os.sep + script[PUBLISHEDTIME]):
                os.makedirs('static'+os.sep+'temp'+os.sep + script[PUBLISHEDTIME])

            # get list of .png or .jpg files
            publishedDir = []
            if os.path.exists('static'+os.sep + script[PUBLISHEDTIME]):
                publishedDir = os.listdir('static'+os.sep + script[PUBLISHEDTIME])
            for fileName in publishedDir:
                fileNameStem, fileNameSuffix = os.path.splitext(fileName)
                if fileNameSuffix in (".png",".jpg"):
                    if not os.path.exists('static'+os.sep+'temp'+os.sep + \
                                          script[PUBLISHEDTIME] + os.sep + fileName):
                        shutil.move('static'+os.sep + script[PUBLISHEDTIME] + os.sep + \
                                     fileName, 'static'+os.sep+'temp'+os.sep + script[PUBLISHEDTIME])

        if os.path.exists('static' +os.sep + script[PUBLISHEDTIME]):
            # remove the directory
            shutil.rmtree('static'+os.sep + script[PUBLISHEDTIME])

# TODO: Windows permissions issue
        if not deleteAll:
            # then restore the directory with .png or .jpg files only
            shutil.move('static'+os.sep+'temp'+os.sep + script[PUBLISHEDTIME], 'static')

    # ----------------------------------------------------------------

    def publishSlidesToHtml(self, tag = None, email=None, publishedTime=None, title=None, author=None):
        if tag:
            tag=urllib.unquote(tag).decode('utf8')

        connection = sqlite3.connect('slides.db')
        cursor = connection.cursor()

        # Take list of slide from slides table
        cursor.execute('SELECT * FROM slides WHERE tag=? AND email=?',(tag,email,))
        slideRecords=cursor.fetchall()
        slideList = []

        # create time stamped temp directory or reuse existing directory
        if type(publishedTime)==type(None) or 0==len(publishedTime.strip()):
            timeStampForPath = self.timestamp()
            f = open("pubTime.txt","w")
            f.write("New:")
            f.write(timeStampForPath)
            f.write(str(type(timeStampForPath)))
            f.close()
        else:
            timeStampForPath = publishedTime
            f = open("pubTime.txt","w")
            f.write("Existing:")
            f.write(timeStampForPath)
            f.write(str(type(timeStampForPath)))
            f.close()

        if not os.path.exists('static/'+timeStampForPath):
            os.makedirs('static/'+timeStampForPath)

        connection = sqlite3.connect('slides.db')
        cursor = connection.cursor()
        cursor.execute('SELECT * FROM scripts WHERE tag=? AND email=?',(tag,email,))
        scriptList = cursor.fetchall()
        for script in scriptList:
            if 0<len(script[PUBLISHEDTIME].strip()):
                self.unpublishScript(script, deleteAll=False)
        cursor.execute('DELETE FROM scripts WHERE tag=? AND email=?', (tag,email,))
        cursor.execute('INSERT INTO scripts VALUES (?,?,?,?,?,?,?,?,?)', (tag,email,timeStampForPath,title,author,"http://slidespeechweb.pagekite.me/static/"+timeStampForPath+"/output.zip","7","8","9"))
        connection.commit()

        # create script.txt
        script = ""
        for slide in slideRecords:
            staticPath, slidesource = os.path.split(slide[SLIDESOURCE])
#            if slidesource.startswith('static'):
#                slidesource = ".."+slidesource[6:]
            slideList.append([slidesource,slide[SLIDETEXT]])
            if 0<len(slidesource):
                script += slidesource+"\n"
            script += slide[SLIDETEXT]+"\n\n"
#         f = codecs.open("static/"+timeStampForPath+"/script.txt", encoding='utf-8', mode='w+')
#         f.write(script)
#         f.close

        # create sequence
        seq = scriptParser.parseTxtFile(script.split("\n"))

        # pickle rules (full sequence)
        f = open("static"+os.sep +timeStampForPath+os.sep +"rules.txt","w")
        cPickle.Pickler(f).dump(seq)
        f.close()

        # make convert.bat
        tagForUrl = urllib.urlencode({'tag':tag,'email':email})

        SlideSpeechConverter.makeConvert(seq.sequence, timeStampForPath, tagForUrl)

        cursor.execute('SELECT * FROM scripts WHERE email=?',(email,))
        scriptList = cursor.fetchall()
        scriptList.sort()

        # Send email with link to published slides
        emailViaGoogle.send_email(tag,
                                  timeStampForPath,
                                  from_addr=GMAIL_LOGIN,
                                  to_addr=email)
        return forms.listScripts(__version__,
                                 "Presentation published. Check e-mail.",
                                 scriptList,
                                 tag,
                                 timeStampForPath,
                                 email)
    publishSlidesToHtml.exposed = True

    # ----------------------------------------------------------------

    def initView(self, pathToPublishedHtml = None, tag = None, email = None):
        # generate log file for this view
        viewLog = self.timestamp()
        #viewLog = "latest"
        logFile = open("static"+os.sep +"logs"+os.sep +viewLog+".log","w")
        logFile.write("Log at "+self.timestamp()+"\n")
        logFile.write("Input: View button for script number "+pathToPublishedHtml+"\n")
        # log any initial statements of the script

        f = open("static/" +pathToPublishedHtml +"/rules.txt","r")

        seq = cPickle.Unpickler(f).load()
        onQofSeq = 0
        while onQofSeq<len(seq.sequence)-2 and \
              0<len(seq.sequence[onQofSeq].answers) and \
              ("[next]"==seq.sequence[onQofSeq].answers[0].answerText or
               "[input]"==seq.sequence[onQofSeq].answers[0].answerText):
            question = seq.sequence[onQofSeq]
            if 0==onQofSeq:
                logFile.write("Reply: "+"\n       ".join(question.questionTexts)+"\n")
            else:
                logFile.write("+      "+"\n       ".join(question.questionTexts)+"\n")
            if "[input]"==seq.sequence[onQofSeq].answers[0].answerText:
                break
            onQofSeq += 1
        logFile.close()

        # save / update record

        connection = sqlite3.connect('slides.db')
        cursor = connection.cursor()
        cursor.execute('SELECT * FROM logs WHERE publishedTime=?', (pathToPublishedHtml,))
        logList = cursor.fetchall()
        if 0<len(logList):
            cursor.execute('DELETE FROM logs WHERE publishedTime=?', (pathToPublishedHtml,))
        cursor.execute('INSERT INTO logs VALUES (?,?,?,?)', (tag,email,pathToPublishedHtml,viewLog,))
        connection.commit()


        return """<html>
            <head>
            <script type="text/javascript">
            function redirect(){
                window.location = """ + '"static/' + pathToPublishedHtml  +'/odpName.htm'+'"\n'+ \
            """}
            </script>
            </head>
            <body onLoad="redirect()">
            </body>
            </html>"""
    initView.exposed = True

    # ----------------------------------------------------------------

    def processInput(self, inputValue = None, tempPath = None):
        # Look up log file for latest view and log new input
        connection = sqlite3.connect('slides.db')
        cursor = connection.cursor()
        cursor.execute('SELECT * FROM logs WHERE publishedTime=?', (tempPath,))
        logList = cursor.fetchall()
        if 0<len(logList):
            #logFile = open("static/logs/"+"latest"+".log","a")
            logFile = open("static"+os.sep +"logs"+os.sep +logList[0][CURRENTLOG]+".log","a")
            logFile.write("Input: "+inputValue+"\n")
            logFile.write("Time:  "+self.timestamp()+"\n")
            if inputValue=="show log":
                #return redirectTo('static/logs/latest.log')
                return redirectTo('static'+os.sep +'logs'+os.sep +logList[0][CURRENTLOG]+'.log')

        f = open("static"+os.sep +tempPath+os.sep +"rules.txt","r")
        seq = cPickle.Unpickler(f).load()
        patterns = [rule.rulePattern for rule in seq.rules]
        inputValue = inputValue.lower()
        for position, pattern in enumerate(patterns):
            match = pattern.match(inputValue)

            # did the pattern match?
            if match:
                link = seq.rules[position].ruleName
                tags = [question.tag for question in seq.sequence]
                for qnum, tag in enumerate(tags):
                    if link == tag:
                        question = seq.sequence[qnum]
                        logFile.write("Reply: "+"\n       ".join(question.questionTexts)+"\n")
                        # pre-build subsequent replies if warranted
                        onQofSeq = qnum
                        while onQofSeq < len(seq.sequence)-2 and \
                              0 < len(seq.sequence[onQofSeq].answers) and \
                              ("[next]"==seq.sequence[onQofSeq].answers[0].answerText or
                               "[input]"==seq.sequence[onQofSeq].answers[0].answerText):
                            onQofSeq += 1
                            question = seq.sequence[onQofSeq]
                            logFile.write("+      "+"\n       ".join(question.questionTexts)+"\n")
                            if "[input]"==seq.sequence[onQofSeq].answers[0].answerText:
                                break
                        # Make list of question file names for navigation
                        questionFileNames = []
                        onImg = 0
                        imageFilePrefix = "Slide"
                        onImgStr = str(onImg)
                        onQ = 0
                        for question in seq.sequence:
                            if len(question.answers)==0:
                                questionFileNames.append(imageFilePrefix+onImgStr)
                                onImg += 1
                                onImgStr = str(onImg)
                                onQ = 0
                            else:
                                onQ += 1
                                questionFileNames.append(imageFilePrefix+onImgStr+"q"+str(onQ))
                        logFile.close()
                        return redirectTo('static' +os.sep + tempPath + os.sep + \
                                          questionFileNames[qnum]+'.htm')
                logFile.close()
                return "No question"

        logFile.close()
        return "No match"
        #return serve_file(os.path.abspath("static/"+tempPath+"/Slide0q3.htm"))
#        return "Process input\n" + inputValue + "\n" + seq.rules[0].ruleName
    processInput.exposed = True

def redirectTo(newUrl):
    return """<html>
    <head>
    <script type="text/javascript">
    function redirect(){
        window.location = """ + '"' + newUrl + '"' + \
    """}
    </script>
    </head>
    <body onLoad="redirect()">
    </body>
    </html>"""

if __name__ == '__main__':
    # CherryPy always starts with app.root when trying to map request URIs
    # to objects, so we need to mount a request handler root. A request
    # to '/' will be mapped to HelloWorld().index().
    cherrypy.config.update({'server.socket_host': '127.0.0.1',
                        'server.socket_port': 10080,
                        'server.thread_pool': 10,
                       })
    config = {'/': {'tools.staticdir.root': os.path.abspath(os.curdir)},
              '/static':{'tools.staticdir.on':True,
                         'tools.staticdir.dir':"static"}}
    cherrypy.quickstart(ScriptAuthoringPage(), config=config)

